# pysql_compiler/catalog.py

from typing import Dict, List, Any


class SemanticError(Exception):
    def __init__(self, error_type: str, position: tuple, message: str):
        self.error_type = error_type
        self.position = position
        self.message = message
       
        super().__init__(
            f"[{self.error_type}, 位置: 行 {self.position[0]} 列 {self.position[1]}, 原因: {self.message}]")


class IndexSchema:
    """存储单个索引的元数据信息"""

    def __init__(self, index_name: str, table_name: str, column_name: str, root_page_id: int | None = None):
        self.index_name = index_name
        self.table_name = table_name
        self.column_name = column_name
        self.root_page_id = root_page_id


class TableSchema:
    """存储单个表结构信息"""

    def __init__(self, table_name: str):
        self.table_name = table_name
        self.columns: Dict[str, str] = {}
        self.column_order: List[str] = []
        self.pages: List[int] = []
        self.indexes: Dict[str, IndexSchema] = {}  # column_name -> IndexSchema
        self.auto_increment_column: str = None
        self.next_auto_increment_value: int = 1
        self.unique_constraints: set = set()
        self.check_constraints: list = []
        self.foreign_keys = []

        self.not_null_columns: set = set()

    def add_column(self, column_name: str, data_type: str):
        if column_name in self.columns:
            raise ValueError(f"Column '{column_name}' already exists in table '{self.table_name}'")
        self.columns[column_name] = data_type.upper()
        self.column_order.append(column_name)

    def add_index(self, index_name: str, column_name: str):
        """为该表添加索引元数据"""
        if not self.has_column(column_name):
            raise ValueError(f"不能在一个不存在的列 '{column_name}' 上创建索引")

        index_schema = IndexSchema(index_name, self.table_name, column_name)
        # 键使用列名，方便查找
        self.indexes[column_name] = index_schema

    def get_column_type(self, column_name: str) -> str:
        return self.columns.get(column_name)

    def has_column(self, column_name: str) -> bool:
        return column_name in self.columns

    def get_index_on_column(self, column_name: str) -> IndexSchema | None:
        return self.indexes.get(column_name)


class Catalog:
    """数据库模式目录，管理所有表的schema和索引"""

    def __init__(self):
        self.tables: Dict[str, TableSchema] = {}
        self.indexes: Dict[str, Dict[Any, List[int]]] = {}

    def create_table(self, table_name: str, columns: List[dict], constraints: List[dict] = None):
        if table_name in self.tables:
            # 如果表已存在，则直接返回，避免重复创建
            return

        schema = TableSchema(table_name)
        # 1. 处理列定义和列级约束
        for col_def in columns:
            col_name = col_def['column_name']['name']
            data_type = col_def['data_type']['name']
            schema.add_column(col_name, data_type)
            if 'constraints' in col_def:
                for constraint in col_def['constraints']:
                    constraint_name = constraint.get('name')
                    if constraint_name == 'AUTO_INCREMENT':
                        schema.auto_increment_column = col_name
                    elif constraint_name == 'UNIQUE' or constraint_name == 'PRIMARY KEY':
                        schema.unique_constraints.add(col_name)
                    elif constraint_name == 'CHECK':  # 添加这部分
                        schema.check_constraints.append(constraint['expression'])


        # 2. 处理表级约束 (从新的 `constraints` 参数获取)
        if constraints:
            for constraint in constraints:
                if constraint.get('name') == 'UNIQUE' and 'columns' in constraint:
                    # 将多列约束作为一个元组添加到集合中
                    schema.unique_constraints.add(tuple(constraint['columns']))

        self.tables[table_name] = schema
        print(f"[Catalog] Table '{table_name}' created with columns: {list(schema.columns.keys())}")

    def create_index(self, index_name: str, table_name: str, column_name: str, root_page_id: int | None = None):
        if index_name in self.indexes:
            raise ValueError(f"Index '{index_name}' already exists.")

        table_schema = self.get_table_schema(table_name)
        if not table_schema:
            raise ValueError(f"Table '{table_name}' does not exist.")
        if not table_schema.has_column(column_name):
            raise ValueError(f"Column '{column_name}' does not exist in table '{table_name}'.")

        index_schema = IndexSchema(index_name, table_name, column_name, root_page_id)
        table_schema.indexes[column_name] = index_schema
        self.indexes[index_name] = {}
        print(f"[Catalog] Index '{index_name}' created on table '{table_name}' column '{column_name}'.")
        return index_schema

    def drop_index(self, index_name: str):
        """从目录中删除索引"""
        if index_name not in self.indexes:
            raise ValueError(f"索引 '{index_name}' 不存在.")

        # 1. 从中心索引列表中删除
        del self.indexes[index_name]

        # 2. 从表的元数据中找到并删除该索引
        found = False
        for table_schema in self.tables.values():
            # 找到拥有该索引的列
            column_to_delete = None
            for col_name, index_schema in table_schema.indexes.items():
                if index_schema.index_name == index_name:
                    column_to_delete = col_name
                    break

            if column_to_delete:
                del table_schema.indexes[column_to_delete]
                found = True
                break

        if not found:
            # 这种情况理论上不应该发生，除非数据不一致
            print(f"[Catalog] 警告: 索引 '{index_name}' 存在于主列表但未关联到任何表.")

        print(f"[Catalog] Index '{index_name}' dropped.")

    def get_table_schema(self, table_name: str) -> TableSchema:
        return self.tables.get(table_name)

    def table_exists(self, table_name: str) -> bool:
        return table_name in self.tables